Add support to clone existing offerings and update them#12357
Add support to clone existing offerings and update them#12357
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #12357 +/- ##
============================================
+ Coverage 17.89% 18.19% +0.29%
- Complexity 16092 16604 +512
============================================
Files 5936 5952 +16
Lines 532734 541105 +8371
Branches 65165 68497 +3332
============================================
+ Hits 95347 98443 +3096
- Misses 426711 431599 +4888
- Partials 10676 11063 +387
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
62368f3 to
3787aaa
Compare
a5a9eb4 to
bb2e90d
Compare
bb2e90d to
7fd4567
Compare
|
This pull request has merge conflicts. Dear author, please fix the conflicts and sync your branch with the base branch. |
…e-edit-existing-offerings
…tris from the source networkmode
There was a problem hiding this comment.
Pull request overview
This PR introduces “clone offering” functionality across the CloudStack API and UI, enabling users to clone existing offerings (compute/service, disk, network, VPC, backup) while overriding selected parameters. It also refactors network/VPC service-capability param building into shared UI helpers and wires the new actions into the offerings UI.
Changes:
- Added new API commands and server implementations for cloning multiple offering types (plus event types and service interfaces).
- Added new UI clone views (compute/disk/backup shown here) and integrated clone actions into the offerings section config.
- Refactored network/VPC service-capability parameter construction into a shared composable and extracted disk offering form into a reusable component.
Reviewed changes
Copilot reviewed 41 out of 41 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| ui/src/views/offering/CloneDiskOffering.vue | New UI flow to clone disk offerings using the shared DiskOfferingForm. |
| ui/src/views/offering/CloneComputeOffering.vue | New UI flow to clone compute/service offerings (includes prefill + submit logic). |
| ui/src/views/offering/CloneBackupOffering.vue | New UI flow to clone backup offerings (zone/provider offering selection + async job polling). |
| ui/src/views/offering/AddVpcOffering.vue | Refactors VPC service capability param building into a shared helper. |
| ui/src/views/offering/AddNetworkOffering.vue | Refactors network service capability param building into a shared helper. |
| ui/src/views/offering/AddDiskOffering.vue | Refactors disk offering creation to use the new shared DiskOfferingForm component. |
| ui/src/config/section/offering.js | Adds new “Clone * Offering” actions in the UI offerings sections. |
| ui/src/composables/useServiceCapabilityParams.js | New shared helper(s) to build serviceCapability/serviceProvider param sets for network/VPC offerings. |
| ui/src/components/offering/DiskOfferingForm.vue | New reusable disk offering form component extracted from the previous view. |
| ui/src/components/CheckBoxSelectPair.vue | Adds support for setting a default select value. |
| ui/public/locales/en.json | Adds i18n strings for clone offering actions and messages. |
| server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java | Adds tests asserting cloned backup offering domain handling. |
| server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java | Updates test mock to support new clone APIs and new base cmd type for network offering creation. |
| server/src/test/java/com/cloud/configuration/ConfigurationManagerImplTest.java | Adds tests around reflection helpers and some clone-related scaffolding. |
| server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java | Implements backup offering clone behavior and registers the command. |
| server/src/main/java/com/cloud/server/ManagementServerImpl.java | Registers new clone commands in management server command list. |
| server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java | Implements VPC offering clone logic, including service/provider/capability reconstruction. |
| api/src/test/java/org/apache/cloudstack/api/command/admin/vpc/CloneVpcOfferingCmdTest.java | Adds unit tests for the new clone VPC offering command. |
| api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CloneServiceOfferingCmdTest.java | Adds unit tests for the new clone service offering command. |
| api/src/test/java/org/apache/cloudstack/api/command/admin/network/CloneNetworkOfferingCmdTest.java | Adds unit tests for the new clone network offering command. |
| api/src/test/java/org/apache/cloudstack/api/command/admin/backup/CloneBackupOfferingCmdTest.java | Adds unit tests for the new clone backup offering command. |
| api/src/main/java/org/apache/cloudstack/backup/BackupManager.java | Adds cloneBackupOffering to the backup manager interface. |
| api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java | Adjusts supported-services behavior for external providers (validation moved server-side). |
| api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CloneVPCOfferingCmd.java | New API command for cloning VPC offerings. |
| api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CloneServiceOfferingCmd.java | New API command for cloning service offerings. |
| api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CloneDiskOfferingCmd.java | New API command for cloning disk offerings. |
| api/src/main/java/org/apache/cloudstack/api/command/admin/network/NetworkOfferingBaseCmd.java | New base command consolidating shared network-offering params/logic. |
| api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java | Refactors create network offering cmd to extend NetworkOfferingBaseCmd. |
| api/src/main/java/org/apache/cloudstack/api/command/admin/network/CloneNetworkOfferingCmd.java | New API command for cloning network offerings. |
| api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ImportBackupOfferingCmd.java | Makes backupManager protected and annotates domainIds param with since. |
| api/src/main/java/org/apache/cloudstack/api/command/admin/backup/CloneBackupOfferingCmd.java | New API command for cloning backup offerings (domainIds resolver support). |
| api/src/main/java/org/apache/cloudstack/api/ApiConstants.java | Adds SOURCE_OFFERING_ID constant. |
| api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java | Adds cloneVPCOffering to VPC provisioning service interface. |
| api/src/main/java/com/cloud/event/EventTypes.java | Adds new event types for offering/backup offering clone. |
| api/src/main/java/com/cloud/configuration/ConfigurationService.java | Adds clone methods and generalizes createNetworkOffering to accept the new base cmd. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
api/src/main/java/org/apache/cloudstack/api/command/admin/backup/CloneBackupOfferingCmd.java
Outdated
Show resolved
Hide resolved
| export default { | ||
| name: 'CreateComputeOffering', | ||
| mixins: [mixinForm], | ||
| components: { |
There was a problem hiding this comment.
The component is registered with name 'CreateComputeOffering', but this view is for cloning. This breaks devtools/component identification and may affect caching/keep-alive behavior if other components rely on the name. Rename it to 'CloneComputeOffering' (or another clone-specific name).
| } | ||
|
|
||
| params.sourceofferingid = this.resource.id | ||
|
|
||
| postAPI('cloneServiceOffering', params).then(json => { | ||
| const message = this.isSystem |
There was a problem hiding this comment.
this.loading is never set to true before calling postAPI('cloneServiceOffering', ...), so the submit-guard (if (this.loading) return) does not prevent double submits and the UI spinner never activates. Set this.loading = true immediately before the API call (and keep the existing finally that resets it).
api/src/main/java/org/apache/cloudstack/api/command/admin/backup/CloneBackupOfferingCmd.java
Outdated
Show resolved
Hide resolved
| }).then(json => { | ||
| this.diskOfferings = json.listdiskofferingsresponse.diskoffering || [] | ||
| if (this.selectedDiskOfferingId === '') { | ||
| this.selectedDiskOfferingId = this.diskOfferings[0].id || '' | ||
| } |
There was a problem hiding this comment.
this.diskOfferings[0].id will throw if listDiskOfferings returns an empty list. Guard with a length check (or use optional chaining) before reading the first element.
| <a-switch v-model:checked="form.encryptdisk" :checked="encryptdisk" @change="val => { encryptdisk = val }" /> | ||
| </a-form-item> | ||
| <a-form-item name="disksizestrictness" ref="disksizestrictness"> | ||
| <template #label> | ||
| <tooltip-label :title="$t('label.disksizestrictness')" :tooltip="apiParams.disksizestrictness.description" /> | ||
| </template> | ||
| <a-switch v-model:checked="form.disksizestrictness" :checked="disksizestrictness" @change="val => { disksizestrictness = val }" /> |
There was a problem hiding this comment.
The switch uses both v-model:checked and an explicit :checked bound to a different data property (encryptdisk). This can desynchronize the UI from form.encryptdisk (especially when initialValues sets a non-default). Prefer binding only via v-model:checked and remove the separate :checked/@change state mirror.
| <a-switch v-model:checked="form.encryptdisk" :checked="encryptdisk" @change="val => { encryptdisk = val }" /> | |
| </a-form-item> | |
| <a-form-item name="disksizestrictness" ref="disksizestrictness"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.disksizestrictness')" :tooltip="apiParams.disksizestrictness.description" /> | |
| </template> | |
| <a-switch v-model:checked="form.disksizestrictness" :checked="disksizestrictness" @change="val => { disksizestrictness = val }" /> | |
| <a-switch v-model:checked="form.encryptdisk" /> | |
| </a-form-item> | |
| <a-form-item name="disksizestrictness" ref="disksizestrictness"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.disksizestrictness')" :tooltip="apiParams.disksizestrictness.description" /> | |
| </template> | |
| <a-switch v-model:checked="form.disksizestrictness" /> |
| <a-switch v-model:checked="form.encryptdisk" :checked="encryptdisk" @change="val => { encryptdisk = val }" /> | ||
| </a-form-item> | ||
| <a-form-item name="disksizestrictness" ref="disksizestrictness"> | ||
| <template #label> | ||
| <tooltip-label :title="$t('label.disksizestrictness')" :tooltip="apiParams.disksizestrictness.description" /> | ||
| </template> | ||
| <a-switch v-model:checked="form.disksizestrictness" :checked="disksizestrictness" @change="val => { disksizestrictness = val }" /> |
There was a problem hiding this comment.
Same issue as encrypt switch: v-model:checked plus explicit :checked backed by disksizestrictness can cause the displayed value to diverge from form.disksizestrictness / initialValues. Bind the switch to a single source of truth (the form model).
| <a-switch v-model:checked="form.encryptdisk" :checked="encryptdisk" @change="val => { encryptdisk = val }" /> | |
| </a-form-item> | |
| <a-form-item name="disksizestrictness" ref="disksizestrictness"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.disksizestrictness')" :tooltip="apiParams.disksizestrictness.description" /> | |
| </template> | |
| <a-switch v-model:checked="form.disksizestrictness" :checked="disksizestrictness" @change="val => { disksizestrictness = val }" /> | |
| <a-switch v-model:checked="form.encryptdisk" /> | |
| </a-form-item> | |
| <a-form-item name="disksizestrictness" ref="disksizestrictness"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.disksizestrictness')" :tooltip="apiParams.disksizestrictness.description" /> | |
| </template> | |
| <a-switch v-model:checked="form.disksizestrictness" /> |
api/src/main/java/org/apache/cloudstack/api/command/admin/backup/CloneBackupOfferingCmd.java
Outdated
Show resolved
Hide resolved
api/src/main/java/org/apache/cloudstack/api/command/admin/backup/CloneBackupOfferingCmd.java
Show resolved
Hide resolved
api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ImportBackupOfferingCmd.java
Show resolved
Hide resolved
api/src/main/java/org/apache/cloudstack/api/command/admin/network/CloneNetworkOfferingCmd.java
Outdated
Show resolved
Hide resolved
api/src/main/java/org/apache/cloudstack/api/command/admin/network/CloneNetworkOfferingCmd.java
Show resolved
Hide resolved
api/src/main/java/org/apache/cloudstack/api/command/admin/network/CloneNetworkOfferingCmd.java
Show resolved
Hide resolved
api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java
Show resolved
Hide resolved
server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java
Outdated
Show resolved
Hide resolved
server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java
Show resolved
Hide resolved
server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java
Outdated
Show resolved
Hide resolved
server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java
Outdated
Show resolved
Hide resolved
server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java
Outdated
Show resolved
Hide resolved
weizhouapache
left a comment
There was a problem hiding this comment.
code lgtm
@Pearl1594
there are import issues
server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java
Outdated
Show resolved
Hide resolved
|
|
@blueorangutan package |
|
@DaanHoogland a [SL] Jenkins job has been kicked to build packages. It will be bundled with no SystemVM templates. I'll keep you posted as I make progress. |
|
Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 16829 |


Description
This PR adds APIs to clone existing offerings - service, system, network, VPC, backup and update certain parameters of the existing offering.
Types of changes
Feature/Enhancement Scale or Bug Severity
Feature/Enhancement Scale
Bug Severity
Screenshots (if appropriate):
How Has This Been Tested?
How did you try to break this feature and the system with this change?